{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "5eabc560-48c2-4109-85f8-ca8da6bd59d2",
   "metadata": {},
   "source": [
    "# **Abstraction**\n",
    "Abstraction is the process of **hiding the implementation details** and **exposing only the relevant functionality** to the user.\n",
    "\n",
    "In Python, abstraction is implemented using the **abc module**.\n",
    "\n",
    "```python\n",
    "from abc import ABC, abstractmethod\n",
    "```\n",
    "- **ABC:** Abstract Base Class\n",
    "- **@abstractmethod:** Decorator to define abstract methods (methods without implementation)\n",
    "\n",
    "| Concept           | Purpose                                          |\n",
    "| ----------------- | ------------------------------------------------ |\n",
    "| `ABC`             | Create abstract base classes                     |\n",
    "| `@abstractmethod` | Force child classes to implement certain methods |\n",
    "\n",
    "\n",
    "## **Why Use Abstraction?**\n",
    "1. Forces subclasses to implement specific methods.\n",
    "2. Defines a clear contract/interface for developers.\n",
    "3. Ensures consistency across different implementations.\n",
    "4. Reduces coupling between components.\n",
    "\n",
    "## **What Abstraction is not?**\n",
    "Not just hiding variables with `__` (**that’s encapsulation i.e. hiding internal data/state**)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "fb1358c7-f768-4e55-acd4-54a9758da584",
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABC, abstractmethod\n",
    "\n",
    "class Animal(ABC):\n",
    "    \n",
    "    @abstractmethod\n",
    "    def make_sound(self):\n",
    "        pass\n",
    "\n",
    "    def sleep(self):\n",
    "        print(\"Sleeping...\")\n",
    "\n",
    "# This will throw an error:\n",
    "# animal = Animal()\n",
    "\n",
    "class Dog(Animal):\n",
    "    def make_sound(self):\n",
    "        print(\"Woof!\")\n",
    "\n",
    "class Cat(Animal):\n",
    "    def make_sound(self):\n",
    "        print(\"Meow!\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "523cd50a-38eb-4292-b6f0-fa1bd64826fa",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Woof!\n",
      "Sleeping...\n"
     ]
    }
   ],
   "source": [
    "d = Dog()\n",
    "\n",
    "d.make_sound()\n",
    "d.sleep()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2fb534a2-1257-4551-bc30-3b29fdf61f88",
   "metadata": {},
   "source": [
    "#### **Observation**\n",
    "1. You can't instantiate Animal directly.\n",
    "2. Any subclass must implement make_sound(), or it'll raise an error.\n",
    "3. Common behavior like sleep() can still be implemented in the base class."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2487d544-2922-4da0-9749-175f4cb64796",
   "metadata": {},
   "source": [
    "## **Real Time Example of Abstraction**\n",
    "\n",
    "You’re building a payment system for an edtech platform that allows students to purchase courses using UPI (Unified Payments Interface). But students use different UPI apps — PhonePe, Google Pay, Paytm, BHIM, etc.\n",
    "\n",
    "Without Abstraction, you’d have scattered logic, tightly coupling your app with every UPI provider’s implementation. \n",
    "Whereas, with abstraction: \n",
    "- You define a common interface for any UPI-based payment processor.\n",
    "- Each UPI app then implements the common logic behind the scenes, hiding differences.\n",
    "\n",
    "Let's do the implementation in three steps:\n",
    "1. Abstract Class (Interface)\n",
    "2. Concrete Implementations\n",
    "3. Usage Layer"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f05d1d8d-edb5-4df7-816f-63fe8449ab1c",
   "metadata": {},
   "source": [
    "#### **Step 1: Abstract Class**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d85c4658-c1b2-49c6-848b-21f01b4807d8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABC, abstractmethod\n",
    "\n",
    "class UPIPaymentProcessor(ABC):\n",
    "    \n",
    "    @abstractmethod\n",
    "    def pay(self, user_id: str, amount: float, upi_id: str):\n",
    "        \"\"\"Process a UPI payment\"\"\"\n",
    "        pass\n",
    "\n",
    "    @abstractmethod\n",
    "    def generate_receipt(self) -> str:\n",
    "        \"\"\"Generate transaction receipt\"\"\"\n",
    "        pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cbaab47e-04e3-4c28-8e5e-145efabb28f8",
   "metadata": {},
   "source": [
    "#### **Step 2: Concrete Implementations**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b05010f7-25fd-4398-8b0e-c25505bb1c84",
   "metadata": {},
   "outputs": [],
   "source": [
    "# PhonePe\n",
    "class PhonePeProcessor(UPIPaymentProcessor):\n",
    "    def pay(self, user_id, amount, upi_id):\n",
    "        print(f\"[PhonePe] Sending ₹{amount} request to {upi_id} for User {user_id}\")\n",
    "        self.transaction_id = f\"PP_{user_id}_TXN001\"\n",
    "    \n",
    "    def generate_receipt(self):\n",
    "        return f\"[PhonePe] Receipt #{self.transaction_id}\"\n",
    "\n",
    "\n",
    "# GooglePay\n",
    "class GooglePayProcessor(UPIPaymentProcessor):\n",
    "    def pay(self, user_id, amount, upi_id):\n",
    "        print(f\"[GPay] Processing ₹{amount} to {upi_id} for User {user_id}\")\n",
    "        self.transaction_id = f\"GPay_{user_id}_TXN002\"\n",
    "    \n",
    "    def generate_receipt(self):\n",
    "        return f\"[GPay] Receipt #{self.transaction_id}\"\n",
    "\n",
    "\n",
    "# Paytm\n",
    "class PaytmProcessor(UPIPaymentProcessor):\n",
    "    def pay(self, user_id, amount, upi_id):\n",
    "        print(f\"[Paytm] ₹{amount} sent to {upi_id} for User {user_id}\")\n",
    "        self.transaction_id = f\"PTM_{user_id}_TXN003\"\n",
    "    \n",
    "    def generate_receipt(self):\n",
    "        return f\"[Paytm] Receipt #{self.transaction_id}\"\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3b236f4c-69cc-41e8-9074-e01be984510d",
   "metadata": {},
   "source": [
    "#### **Step 3: Usage Layer**"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3a89d7af-1977-4b78-80ea-42a570dd0099",
   "metadata": {},
   "outputs": [],
   "source": [
    "def checkout(processor: UPIPaymentProcessor, user_id: str, amount: float, upi_id: str):\n",
    "    processor.pay(user_id, amount, upi_id)\n",
    "    receipt = processor.generate_receipt()\n",
    "    print(receipt)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "2129e46f-2b34-4a4e-aec4-9525d8ae5a47",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[PhonePe] Sending ₹999.0 request to user@ibl for User U101\n",
      "[PhonePe] Receipt #PP_U101_TXN001\n"
     ]
    }
   ],
   "source": [
    "# Run various PhonePe payment processors\n",
    "checkout(PhonePeProcessor(), \"U101\", 999.0, \"user@ibl\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "58efce00-8d18-4779-baea-55957d8ab343",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[GPay] Processing ₹499.0 to user@okhdfcbank for User U102\n",
      "[GPay] Receipt #GPay_U102_TXN002\n"
     ]
    }
   ],
   "source": [
    "# Run various GooglePay payment processors\n",
    "checkout(GooglePayProcessor(), \"U102\", 499.0, \"user@okhdfcbank\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "29f44b79-f918-407f-b49b-f227aceb79fd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Paytm] ₹799.0 sent to user@paytm for User U103\n",
      "[Paytm] Receipt #PTM_U103_TXN003\n"
     ]
    }
   ],
   "source": [
    "# Run various Paytm payment processors\n",
    "checkout(PaytmProcessor(), \"U103\", 799.0, \"user@paytm\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
